/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
	\file nodeIconManager.cxx
	\brief class implemenation XML parser

	\author Cory Dixon
*/

#include "networkLayer.h"
#include "clientList.h"
#include "vcgui3.h"
#include "utilities.h"
#include "vcConfig.h"

using namespace std;

/*<---Global Variables---->*/
extern GuiStruct guiStr;

//-------------------------------------------
// look-up table for capacity to width
#define LT_N_RATE 9
#define LT_N_ITEMS 2
// Mbps -> line width
float CapToLineWidth_table[LT_N_RATE][LT_N_ITEMS] = {  {1, 2},
                                                       {2, 2.5},
                                                       {5.5,4},
                                                       {11, 7},
                                                       {18, 8},
                                                       {24, 9},
                                                       {36, 10},
                                                       {48, 11},
                                                       {54, 12} 
                                                    };

// convert from Mb/s to a line width
float NetworkLayerManager::capToLineWidth(float cap)
{
	if( cap < CapToLineWidth_table[0][0] )
		return CapToLineWidth_table[0][1];

	int ind = 1;
	float deltaC;
	float deltaW;

	while( ind < LT_N_RATE ){
		if( CapToLineWidth_table[ind][0] > cap ) {
			deltaC = CapToLineWidth_table[ind][0] - CapToLineWidth_table[ind-1][0];
			deltaW = CapToLineWidth_table[ind][1] - CapToLineWidth_table[ind-1][1];
			return deltaW / deltaC * (cap - CapToLineWidth_table[ind-1][0]) + CapToLineWidth_table[ind-1][1];
		}
		ind++;
	}
	return CapToLineWidth_table[LT_N_RATE-1][1];
}

float NetworkLayerManager::rateToLineWidth(float cap, float rate, float capW)
{
	if( cap <= 0 )
		return 0;

	if( capW < 0 )
		capW = capToLineWidth(cap);

	// figure out fraction of capacity the rate is
	cap *= 131072.0; // change of units from Mb/s to B/s

	double lineW = capW * rate / cap * 128.0;
	//cout << "NetworkLayerManager::rateToLineWidth - lineW=" << lineW << " cap=" << cap << endl;

	return lineW;
}

NetworkLayerManager::NetworkLayerManager()
{
}

NetworkLayerManager::~NetworkLayerManager()
{
}

void NetworkLayerManager::init(GPSMap *map)
{
	// init base class
	LayerManager::init(map);

	// get my layer group
	layerG = GOO_CANVAS_ITEM(gps_map->getLayerG(GPSMap::NETWORK_LAYER));

	// set alpha value
	set_alpha(NETWORK_ALPHA);

	// init link values to known states
	for(int iInd=0; iInd < MAX_CLIENTS; iInd++ ) {
		for(int jInd=0; jInd < MAX_CLIENTS; jInd++ ) {
			links[iInd][jInd].capacity    = 0;
			links[iInd][jInd].usage       = 0;
			links[iInd][jInd].max_usage   = 0;
			links[iInd][jInd].cap_color   = 0x00FF0000 | alpha;          // rgba
			links[iInd][jInd].maxU_color  = 0x99000000 | (alpha); // rgba
			links[iInd][jInd].usage_color = 0x0000FF00 | (alpha); // rgba
		}
	}

}

void NetworkLayerManager::update_links() 
{
	Client * a_client;
	int clientInd = 0;
	
	if( guiStr.shm_ptr == NULL ) {
		cout << "NetworkLayerManager::update_links() -- shm_ptr == NULL" << endl;
		return;
	}

	while( (a_client = guiStr.shm_ptr->clients.getClient(clientInd)) != NULL) 
	{
			//---------- CMD_LINK_STATUS ----------//
			if( a_client->capabilities.find(CMD_LINK_STATUS) ) 
			{
				//-- go through neighbor links
				for(int nInd=0; nInd < MAX_CLIENTS; nInd++ ) {
					//cout << "\tclient=" << i << " last_rx=" << a_client->linkstatus[i].last_rx << endl;
					if( a_client->linkstatus[nInd].last_rx > 0 && a_client->state.gui) {

						NetworkLink *linkPtr = &(links[clientInd][nInd]);

						// link status values
						linkPtr->capacity = a_client->linkstatus[nInd].channelCap;
						linkPtr->usage    = a_client->linkstatus[nInd].rateTx + a_client->linkstatus[nInd].rateRx;

						if( linkPtr->usage > linkPtr->max_usage )
							linkPtr->max_usage = linkPtr->usage ;

						if(  a_client->linkstatus[nInd].last_rx > 30 )
							linkPtr->cap_color = 0x00000000 | alpha;
						else if(  a_client->linkstatus[nInd].last_rx > 10 )
							linkPtr->cap_color = 0xFF000000 | alpha;
						else
							linkPtr->cap_color = 0x00FF0000 | alpha;

						if(  a_client->linkstatus[nInd].last_rx > 10 )
							linkPtr->timed_out = true;
						else
							linkPtr->timed_out = false;

						// see if we can get a client for this neighbor
						Client* b_client = get_client(a_client, nInd);
						if( !b_client || !b_client->state.gui)
							continue;

						// loop through and convert lat/lon points to image 
						linkPtr->x1 = a_client->telemetry.gps.pos.lon;
						linkPtr->y1 = a_client->telemetry.gps.pos.lat;
						linkPtr->x2 = b_client->telemetry.gps.pos.lon;
						linkPtr->y2 = b_client->telemetry.gps.pos.lat;

						// transform into window
						gps_map->geoToLayer(GPSMap::NETWORK_LAYER, linkPtr->x1, linkPtr->y1);
						gps_map->geoToLayer(GPSMap::NETWORK_LAYER, linkPtr->x2, linkPtr->y2);

						linkPtr->capWidth  = capToLineWidth(linkPtr->capacity);
						linkPtr->useWidth  = rateToLineWidth(linkPtr->capacity, linkPtr->usage, linkPtr->capWidth);
						//linkPtr->maxUWidth = rateToLineWidth(linkPtr->capacity, linkPtr->max_usage, linkPtr->capWidth);
						//cout << "link: client=" << a_client->name << " neighbor=" << b_client->name << endl;
						//cout << "capWidth=" << linkPtr->capWidth << " useWidth=" << linkPtr->useWidth << " maxUWidth=" << linkPtr->maxUWidth << endl;

						// see if there is a drawn link between us. if not create one,
						// if there is, update its properties
						if( linkPtr->linkG == NULL ) {
							linkPtr->name = b_client->name + String(" -> ") + a_client->name;
							create_link_lines(linkPtr);
						} else 
							update_link_lines(linkPtr);
					}
				}
			}

		/*--------- check next client ----------*/
		clientInd++;
	}
}

void NetworkLayerManager::updateMap()
{
	//cout << "NetworkLayerManager::updateMap()" << endl;

	update_links();
	//update_links_TEST();
}

void NetworkLayerManager::translate_from_zoomChange(double zoom_factor)
{
	GooCanvasPoints *points;

	// for network layer manager, the first children are the link groups
	for( int ind_p=0; ind_p < goo_canvas_item_get_n_children(layerG); ind_p++ ) {
		GooCanvasItem* l = goo_canvas_item_get_child(layerG, ind_p);

		if( l ) {
			// children of the link group are lines
			for(int ind_c=0; ind_c < goo_canvas_item_get_n_children(l); ind_c++ ) {
				GooCanvasItem* line = goo_canvas_item_get_child(l, ind_c);

				// now we have a line, work with it
				if( line ) {
					// get current transform and line points
					g_object_get (G_OBJECT (line), "points", &points, NULL);

					// loop through all points and scale their location
					for(int p=0; p < 2*points->num_points; p++ )
						points->coords[p] *= zoom_factor;

					// set all points
					g_object_set (G_OBJECT (line), "points", points, NULL);
				}
			}
		}
	}
}

Client* NetworkLayerManager::get_client(Client * a_client, int linkInd)
{
	in_addr addr;

	// if we don't know IP, it is not cooperative and thus we don't know GPS
	if( a_client->linkstatus[linkInd].ip[0] == 0 ) 
		return NULL;

	addr.s_addr = *((in_addr_t *)a_client->linkstatus[linkInd].ip);
	return guiStr.shm_ptr->clients.getClient(addr);
}

void NetworkLayerManager::create_link_lines(NetworkLink *linkPtr)
{
	// make sure we are not duplicating
	if( linkPtr->linkG )
		return;

	if( linkPtr->capWidth > 0 && linkPtr->useWidth > 0 ) {
		// get link group
		linkPtr->linkG = goo_canvas_group_new( layerG, NULL );

		// make capacity line
		linkPtr->capacity_line = goo_canvas_polyline_new_line( linkPtr->linkG,
				linkPtr->x1, linkPtr->y1, linkPtr->x2, linkPtr->y2,
				"line-width", linkPtr->capWidth,
				"stroke-color-rgba", linkPtr->cap_color,
				NULL);

		
		gps_map->addLayer( linkPtr->name, linkPtr->linkG, (GooCanvasItem*)GPSMap::NETWORK_LAYER);

		// set the layer as active and shown
		gps_map->setLayerListShown( linkPtr->linkG, true );

		// make sure parent manager layer is shown
		gps_map->toggleLayerShown(GPSMap::NETWORK_LAYER, true);

	/*
		// make max usage line
		linkPtr->max_usage_line = goo_canvas_polyline_new_line( linkPtr->linkG,
				linkPtr->x1, linkPtr->y1, linkPtr->x2, linkPtr->y2,
				"line-width", linkPtr->maxUWidth,
				"stroke-color-rgba", linkPtr->maxU_color,
				NULL);

	*/
		// make usage line
		linkPtr->usage_line = goo_canvas_polyline_new_line( linkPtr->linkG,
				linkPtr->x1, linkPtr->y1, linkPtr->x2, linkPtr->y2,
				"line-width", linkPtr->useWidth,
				"stroke-color-rgba", linkPtr->usage_color,
				NULL);
	}
}

void NetworkLayerManager::update_link_lines(NetworkLink *linkPtr)
{
	GooCanvasPoints *points;
	if( linkPtr->capWidth > 0 && linkPtr->useWidth > 0 )  {

		// update the capacity line
		//  -- start location
		//  - stop  location
		//  - width  location
		//  - color  location
		g_object_get (linkPtr->capacity_line, "points", &points, NULL);
		points->coords[0] = linkPtr->x1;
		points->coords[1] = linkPtr->y1;
		points->coords[2] = linkPtr->x2;
		points->coords[3] = linkPtr->y2;
		g_object_set (linkPtr->capacity_line, "points", points, "line-width", linkPtr->capWidth, "stroke-color-rgba", linkPtr->cap_color, NULL);

		/*
		// update the max usage line
		//  - start location
		//  - stop  location
		//  - width  location
		g_object_get (linkPtr->max_usage_line, "points", &points, NULL);
		points->coords[0] = linkPtr->x1;
		points->coords[1] = linkPtr->y1;
		points->coords[2] = linkPtr->x2;
		points->coords[3] = linkPtr->y2;
		g_object_set (linkPtr->max_usage_line, "points", points, "line-width", linkPtr->maxUWidth, NULL);

*/
		// update the usage line
		//  - start location
		//  - stop  location
		//  - width  location
		g_object_set (linkPtr->usage_line, "points", points, "line-width", linkPtr->useWidth, NULL);
	}
}

void NetworkLayerManager::set_alpha(double a)
{
	if( a > 1 ) a = 1;
	else if(a < 0) a = 0;
	alpha = Round( a * 255.0 );
}
